#include "MutaStrangeAttractor.h"

#include "MutaCurve.h"
#include "qwt/qwt_curve_fitter.h"
#include <cmath>
#include <stdlib.h>
#include <QGLFormat>
#include <QImage>
#include <QDebug>
#include <QTime>

MutaStrangeAttractor::MutaStrangeAttractor(QString owner,QPointF p1,QPointF p2,int layer) :
    MutaObject(owner.append(QString().number(MutaObject::getObjectCount())),Muta::Curve,owner),
    QGraphicsPathItem(),
    layer(layer),
    time(0),
    boxColor(155,255,255,10)
{
    setPos(p1);
    setFlag( QGraphicsItem::ItemIsSelectable, true );
    setFlag( QGraphicsItem::ItemIsMovable, true );
    setFlag(QGraphicsItem::ItemSendsGeometryChanges);
    addControlPoint(p1);
    addControlPoint(p1+QPointF(30,0));

    boxBrush.setColor(boxColor);
    boxBrush.setStyle(Qt::SolidPattern);
    boxPen.setWidth(1);
    boxPen.setColor(boxColor);

    //srand((unsigned) time(0));
    iters = 200;
    sensitivity = 0.02;
    dispersion = 90.0;
}

void MutaStrangeAttractor::buildPath()
{
    QPainterPath path;
    QPolygonF polygon;
    QPolygonF sPolygon;
    QPolygonF sPoly;
    for(int i=0;i<controlPoints.size();i++) {
        polygon.append(controlPoints.at(i)->pos());
    }

    QwtSplineCurveFitter curveFitter;
    polygon = curveFitter.fitCurve(polygon);
    path.addPolygon(polygon);

    if(controlPoints.size() >1) {
        if(boundingRect().width() * boundingRect().height() > 10) {
            sPoly = renderAttractor();
           // qDebug() << sPoly.size();


            path.moveTo(sPoly.at(0));
            for(int i=1;i<sPoly.size();i++) {
                //polygon.append(sPoly.at(i));
                if(sPoly.at(i).x() < sPoly.at(i-1).x()) {
                    sPolygon = curveFitter.fitCurve(sPolygon);
                    path.addPolygon(sPolygon);
                    sPolygon.clear();
                    path.moveTo(sPoly.at(i));
                    sPolygon.append(sPoly.at(i));
                } else {
                    sPolygon.append(sPoly.at(i));
                }
            }
        }
    }
    setPath(path);
}

int MutaStrangeAttractor::type() const
{
    return mutaType;
}

void MutaStrangeAttractor::addControlPoint(QPointF point) {
    controlPoints.append(new ControlPoint(Muta::quantizePoint(point,Muta::BEAT_PIXELS,15)-this->pos()+QPointF(0,7.5),controlPoints.size(),-5,-5,10,10,this));
    QObject::connect(controlPoints.at(controlPoints.size() -1),SIGNAL(mouseMoved(QPointF,int)),this,SLOT(cPointMoved(QPointF,int)));

    buildPath();
    setColor();
}

void MutaStrangeAttractor::cPointMoved(QPointF pos,int num)
{
    controlPoints.at(num)->setPos(Muta::quantizePoint(pos,Muta::BEAT_PIXELS,15));
    buildPath();
    setColor();
}

void MutaStrangeAttractor::setPos(const QPointF &pos)
{
    QGraphicsPathItem::setPos(Muta::quantizePoint(pos,Muta::BEAT_PIXELS,15)+QPointF(0,7.5));
    emit posChanged(objectID,pos);
}


void MutaStrangeAttractor::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
    QGraphicsPathItem::mouseMoveEvent(event);
    setPos(this->pos());
}

void MutaStrangeAttractor::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    /*
    painter->drawPixmap(
                (-0.5*(controlPoints.at(1)->x() - controlPoints.at(0)->x()))+((controlPoints.at(1)->x() - controlPoints.at(0)->x())*0.5),
                (-0.5*(controlPoints.at(1)->y() - controlPoints.at(0)->y()))+((controlPoints.at(1)->y() - controlPoints.at(0)->y())*0.5),
                ((controlPoints.at(1)->x() - controlPoints.at(0)->x())),
                //((controlPoints.at(1)->y() - controlPoints.at(0)->y())),
                60,
                sMap
    );
    */
    //painter->drawPixmap(0,-1 *dispersion,sMap);
    QGraphicsPathItem::paint(painter,option,widget);
}

void MutaStrangeAttractor::setFinalPoint(QPointF p)
{
    controlPoints.last()->setPos(Muta::quantizePoint(p,Muta::BEAT_PIXELS,15) - this->pos()+QPointF(0,7.5));
    QPointF curvePoint = (controlPoints.last()->pos() - controlPoints.at(controlPoints.size()-2)->pos())*0.5 + controlPoints.at(controlPoints.size()-2)->pos();
    buildPath();
    setColor();
}

void MutaStrangeAttractor::setColor()
{
    QColor fillColor;
    QColor firstCPointColor;
    QColor curvePColor;
    switch(layer) {
        case 0:
            fillColor = QColor(227,244,247,255);
            firstCPointColor = QColor(140,204,240,255);
            curvePColor = QColor(64,187,236,255);
            break;
        case 1:
            fillColor = QColor(216,191,215,255);
            firstCPointColor = QColor(194,118,177,255);
            curvePColor = QColor(124,40,125,255);
            break;
        case 2:
            fillColor = QColor(255,153,153);
            firstCPointColor = QColor(242, 99, 75,255);
            curvePColor = QColor(237, 31,36,255);
            break;
        case 3:
            fillColor = QColor(238,232,169,255);
            firstCPointColor = QColor(179,210,53,255);
            curvePColor = QColor(86,107,48,255);
            break;
        case 4:
            fillColor = QColor(	255,204,153,255);
            firstCPointColor = QColor(210,106,40,255);
            curvePColor = QColor(139,70,31,255);
            break;
    }

    QBrush brush;
    brush.setColor(fillColor);
    brush.setStyle(Qt::SolidPattern);

    QPen pen;
    pen.setJoinStyle(Qt::RoundJoin);
    pen.setCapStyle(Qt::RoundCap);
    pen.setBrush(brush);
    pen.setWidth(0.1);
    pen.setColor(fillColor);
    setPen(pen);
    pen.setWidth(0.1);

    for(int i=1;i<controlPoints.size();i++) {
        controlPoints.at(i)->setPen(pen);
        controlPoints.at(i)->setBrush(brush);
    }

    brush.setColor(firstCPointColor);
    pen.setBrush(brush);
    pen.setWidth(1);
    pen.setColor(firstCPointColor);
    controlPoints.at(0)->setPen(pen);
    controlPoints.at(0)->setBrush(brush);

    brush.setColor(curvePColor);
    pen.setBrush(brush);
    pen.setWidth(0.01);
    pen.setColor(QColor(0,0,0,255));
    pen.setColor(curvePColor);
}

QStringList MutaStrangeAttractor::getProperties()
{
    QStringList properties;
    properties << "ml" << owner << QString::number(x()) << QString::number(y()) << QString::number(layer);
    for(int i=0;i<controlPoints.size();i++) {
        properties << QString::number(controlPoints.at(i)->x()) << QString::number(controlPoints.at(i)->y());
    }

    return properties;
}

int MutaStrangeAttractor::getLayer()
{
    return layer;
}

QVariant MutaStrangeAttractor::itemChange(GraphicsItemChange change, const QVariant &value) {
    QVariant variant = QGraphicsPathItem::itemChange(change, value);

    if(change == QGraphicsItem::ItemPositionChange || change == QGraphicsItem::ItemPositionHasChanged)
    {
        variant.setValue(Muta::quantizePoint(variant.toPointF(),Muta::BEAT_PIXELS,15)+QPointF(0,7.5));
    }

    return variant;
}

QPolygonF MutaStrangeAttractor::renderAttractor()
{
    int width = boundingRect().width();
    int height = dispersion*2;
    //QPixmap map(width,height);
    QPolygonF poly;
    //map.fill(Qt::transparent);
    //sPainter.begin(&map);
    //sPainter.setRenderHints(QPainter::Antialiasing);

    QColor attractorColor = QColor(255,255,255,255);
    attractorColor.setAlpha(255);
    QPen attractorPen(attractorColor);
    attractorPen.setWidth(1.0);
    //sPainter.setPen(attractorPen);
    //sPainter.setBrush(attractorColor);

    QTime time = QTime::currentTime();
    qsrand((uint)time.msec());

    double minX = -2.0;
    double minY = -2;

    double maxX = 2.0;
    double maxY = 2.0;

    double minA = acos( (((float) rand() / (float) RAND_MAX) * 2) / 2.0 );
    double maxA = acos( (((float) rand() / (float) RAND_MAX/2) * 2) / 2.0 );
    double minB = acos( (((float) rand() / ((float) RAND_MAX) ) - 1) / 2.0 );
    double maxB = acos( (((float) rand() / ((float) RAND_MAX/2) ) - 1) / 2.0 );
    double minC = acos( (((float) rand() / ((float) RAND_MAX) ) - 1) / 2.0 );
    double maxC = acos( (((float) rand() / ((float) RAND_MAX/2) ) - 1) / 2.0 );
    double minD = acos( (((float) rand() / ((float) RAND_MAX) )) / 2.0 );
    double maxD = acos( (((float) rand() / ((float) RAND_MAX/2) )) / 2.0 );

        double p = 0;
        double a = cos(minA + p * (maxA - minA)) * 2.0;
        double b = cos(minA + p * (maxB - minB)) * 4.0;
        double c = cos(minC + p * (maxC - minC)) * 2.0;
        double d = cos(minD + p * (maxD - minD)) * 4.0;
        //a = 1.5;
        //b = -1.8;
        //c = 1.6;
        //d = 0.9;

        double x = boundingRect().width()*0.5;
        double y = boundingRect().height()*0.5;

        for(int j = 0; j < iters; j++)
        {
            double xn = sin(a * y) + c * cos(a * x);
            double yn = sin(b * x) + d * cos(b * y);
            x = xn;
            y = yn;

            int xi = static_cast<int>((x - minX) * width / (maxX - minX));
            int yi = static_cast<int>((y - minY) * height / (maxY - minY));
            if(xi >= 0 && xi < width && yi >= 0 && yi < height )
            {
                //sPainter.drawPoint(xi, yi);
                poly.append(QPointF(xi,yi - dispersion));
            }
        }
    //sPainter.end();

    return poly;
}
